#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <experimental/filesystem>
#include "bundle.h"
#include <jsoncpp/json/json.h>
#include <memory>

namespace Cloud
{
    namespace fs = std::experimental::filesystem;
    
    class FileUtils
    {
    public:
        FileUtils(const std::string &filePath) : _filePath(filePath) {}
        int64_t getSize() const
        {
            struct stat file_info;
            if (stat(_filePath.c_str(), &file_info) == -1)
            {
                perror("FileUtils::GetSize::stat");
                return -1;
            }
            return file_info.st_size;
        }

        time_t getMTime() const
        {
            struct stat file_info;
            if (stat(_filePath.c_str(), &file_info) == -1)
            {
                perror("FileUtils::GetMTime::stat");
                return -1;
            }
            return file_info.st_mtim.tv_sec;
        }

        time_t getATime() const
        {
            struct stat file_info;
            if (stat(_filePath.c_str(), &file_info) == -1)
            {
                perror("FileUtils::GetATime::stat");
                return -1;
            }
            return file_info.st_atim.tv_sec;
        }

        std::string getFileName() const
        {
            size_t pos = _filePath.find_last_of('/');
            if (pos == std::string::npos)
                return _filePath;
            return _filePath.substr(pos + 1);
        }

        bool getContentFromPos(std::string *pContent, size_t pos, size_t len) const
        {
            if (!pContent)
                return false;
            size_t size = getSize();
            if (size == -1)
                return false;
            if (size > pos && len > size - pos) // pos + len > size
            // 比如大小为1时: pos=0, len=1可以; pos=1, len=1不行
            {
                std::cerr << "GetContentFromPos: pos + len > size\n";
                return false;
            }

            std::ifstream ifs(_filePath, std::ios::binary);
            if (!ifs)
            {
                std::cerr << "GetContentFromPos: open file error\n";
                return false;
            }

            ifs.seekg(pos, std::ios_base::beg);

            pContent->resize(len);

            ifs.read(&((*pContent)[0]), len);
            if (!ifs)
            {
                std::cerr << "GetContentFromPos: read file error\n";
                return false;
            }
            // ifstream 出作用域自动close(属于RAII)
            return true;
        }

        bool getContent(std::string *pContent) const
        {
            return getContentFromPos(pContent, 0, getSize());
        }

        bool setContent(const std::string &content)
        {
            std::ofstream ofs(_filePath, std::ios::binary);
            if (!ofs)
            {
                std::cerr << "SetContent: open file error\n";
                return false;
            }

            ofs.write(&content[0], content.size());

            if (!ofs)
            {
                std::cerr << "SetContent: write file error\n";
                return false;
            }

            return true;
        }

        bool compress(const std::string &dst) const
        {
            std::string org;
            if (!getContent(&org))
                return false;

            std::string packed_text = bundle::pack(bundle::LZIP, org);
            if (packed_text.empty() && !org.empty())
            {
                std::cerr << "DeCompress: pack error\n";
                return false;
            }
            return FileUtils(dst).setContent(packed_text);
        }

        bool deCompress(const std::string &dst) const
        {
            std::string org;
            if (!getContent(&org))
                return false;

            std::string unpacked_text = bundle::unpack(org);
            if (unpacked_text.empty() && !org.empty())
            {
                std::cerr << "DeCompress: unpack error\n";
                return false;
            }
            return FileUtils(dst).setContent(unpacked_text);
        }

        bool exists() const
        {
            return fs::exists(_filePath);
        }

        bool remove()
        {
            return !exists() || fs::remove(_filePath);
        }

        bool createDirectory()
        {
            if (exists())
                return true;
            return fs::create_directories(_filePath);
        }

        void scanDirectory(std::vector<std::string> *pArr)
        {
            if (!pArr)
                return;
            for (auto &p : fs::directory_iterator(_filePath))
            {
                if (fs::is_directory(p))
                    FileUtils(p.path().string()).scanDirectory(pArr); // 递归一下, 等栈溢出了再改非递归
                else
                    pArr->push_back(p.path().string());
            }
        }

    private:
        std::string _filePath;
    };

    class JsonUtils
    {
    public:
    static bool serialize(const Json::Value &root, std::string *pStr)
    {
        if (!pStr)
            return false;
        std::unique_ptr<Json::StreamWriter> pWriter(Json::StreamWriterBuilder().newStreamWriter());
        std::stringstream sout;
        if (pWriter->write(root, &sout) || !sout) // Json::StreamWriter::write的返回 : zero on success (For now, we always return zero, so check the stream instead.)
        	return false;
        *pStr = sout.str();
        return true;
    }
        static bool deSerialize(const std::string &str, Json::Value *pRoot)
        {
            if (!pRoot)
                return false;
            std::unique_ptr<Json::CharReader> pReader(Json::CharReaderBuilder().newCharReader());
            std::string errs;
            if (!pReader->parse(&str[0], &str[0] + str.size(), pRoot, &errs))
            {
                std::cerr << "DeSerialize::parse error: " << errs << std::endl;
                return false;
            }
            return true;
        }
    };
}
